// Shader Variable
// (c) jimon game studio

#include "CShaderVariable_OpenGL.h"
#include "ILogManager.h"

#ifdef JE_RENDER_OPENGL

#include "CShaderMaterial_OpenGL.h"

namespace je
{
	namespace video
	{
		#define JE_GET_CTX Location[CurrentRender->GetShadersGlobalTechnique()]
		#define JE_CHECK_CTX if((JE_GET_CTX != -1) && (CurrentWorkMode!=OWM_11))

		#define JE_SET_UNIFORM(__DataArray,__DataSize,__DataUniformType,__SetupMethod,__SetupCode)\
			JE_CHECK_CTX\
				if(static_cast<CShaderMaterial_OpenGL*>(CurrentShaderMaterial)->IsBinded())\
				{\
					Extensions->__SetupMethod __SetupCode ;\
				}\
				else\
				{\
					if(EnableCache)\
					{\
						CShaderMaterial_OpenGL::ShaderSetupUniform * SetupUniform = NULL;\
						JENEW(SetupUniform,CShaderMaterial_OpenGL::ShaderSetupUniform)\
						SetupUniform->Location = JE_GET_CTX;\
						SetupUniform->SP = CurrentSP[CurrentRender->GetShadersGlobalTechnique()];\
						SetupUniform->DataType = CShaderMaterial_OpenGL::__DataUniformType;\
						SetupUniform->DataSize = __DataSize;\
						JENEW_A(SetupUniform->Data,u8,SetupUniform->DataSize)\
						memcpy(SetupUniform->Data,__DataArray,SetupUniform->DataSize);\
						static_cast<CShaderMaterial_OpenGL*>(CurrentShaderMaterial)->GetShaderSetupUniformList().InsertElement(SetupUniform);\
					}\
				}

		//#define JE_SET_UNIFORM(__DataArray,__DataSize,__DataUniformType,__SetupMethod,__SetupCode)\
		//	JE_CHECK_CTX\
		//		{\
		//			Extensions->UseProgram(CurrentSP[CurrentRender->GetShadersGlobalTechnique()]);\
		//			Extensions->__SetupMethod __SetupCode ;\
		//		}\

		//! Constructor
		CShaderVariable_OpenGL::CShaderVariable_OpenGL(IRender * Render,IShaderMaterial * ShaderMaterial,CRender_OpenGL_Extensions * NewExtensions,const jeStringc & NewName,GLhandleARB * SP,OpenGLWorkMode WorkMode,u1 IgnoreLocationErrors)
			:CurrentRender(Render),CurrentShaderMaterial(ShaderMaterial),Extensions(NewExtensions),Name(NewName),CurrentSP(SP),EnableCache(false),CurrentWorkMode(WorkMode),CurrentIgnoreLocationErrors(IgnoreLocationErrors)
		{
			GetLocations();
		}

		//! Destructor
		CShaderVariable_OpenGL::~CShaderVariable_OpenGL()
		{
			static_cast<CShaderMaterial_OpenGL*>(CurrentShaderMaterial)->RemoveVariable(this);
		}

		//! Get Shader Variable Name
		const jeStringc & CShaderVariable_OpenGL::GetName()
		{
			return Name;
		}

		//! Set Matrix
		void CShaderVariable_OpenGL::SetMatrix(const jeMatrix4f & Matrix)
		{
			
			JE_SET_UNIFORM(Matrix.Element,sizeof(jeMatrix4f),SSUDT_MATRIX_44F,SetUniformMatrix4fv,(JE_GET_CTX,1,true,Matrix.Element))
		}

		//! Set Matrix 3x3 (set up 3x3 part of 4x4 matrix)
		void CShaderVariable_OpenGL::SetMatrix3x3(const jeMatrix4f & Matrix)
		{
			// TODO
			JE_SET_UNIFORM(Matrix.Element,sizeof(jeMatrix4f),SSUDT_MATRIX_33F,SetUniformMatrix3fv,(JE_GET_CTX,1,true,Matrix.Element))
		}

		//! Set s32
		void CShaderVariable_OpenGL::Sets32(const s32 * Data,s32 Size)
		{
			JE_SET_UNIFORM(Data,Size*sizeof(s32),SSUDT_ARRAY_S32,SetUniform1iv,(JE_GET_CTX,Size,Data))
		}

		//! Set f32
		void CShaderVariable_OpenGL::Setf32(const f32 * Data,s32 Size)
		{
			JE_SET_UNIFORM(Data,Size*sizeof(f32),SSUDT_ARRAY_F32,SetUniform1fv,(JE_GET_CTX,Size,Data))
		}

		//! Set Vector Array
		void CShaderVariable_OpenGL::SetVector(const jeVector3df * Vector,s32 Size)
		{
			JE_SET_UNIFORM(Vector,Size*sizeof(jeVector3df),SSUDT_ARRAY_VECTOR_3DF,SetUniform3fv,(JE_GET_CTX,Size,(const GLfloat*)Vector))
		}

		//! Set Vector
		void CShaderVariable_OpenGL::SetVector(const jeVector3df & Vector)
		{
			JE_SET_UNIFORM(&Vector,sizeof(jeVector3df),SSUDT_VECTOR_3DF,SetUniform3fp,(JE_GET_CTX,Vector.X,Vector.Y,Vector.Z))
		}

		//! Set Vector
		void CShaderVariable_OpenGL::SetVector(const jeVector3di & Vector)
		{
			JE_SET_UNIFORM(&Vector,sizeof(jeVector3di),SSUDT_VECTOR_3DI,SetUniform3ip,(JE_GET_CTX,Vector.X,Vector.Y,Vector.Z))
		}

		//! Set Vector
		void CShaderVariable_OpenGL::SetVector(const jeVector2df & Vector)
		{
			JE_SET_UNIFORM(&Vector,sizeof(jeVector2df),SSUDT_VECTOR_2DF,SetUniform2fp,(JE_GET_CTX,Vector.X,Vector.Y))
		}

		//! Set Vector
		void CShaderVariable_OpenGL::SetVector(const jeVector2di & Vector)
		{
			JE_SET_UNIFORM(&Vector,sizeof(jeVector2di),SSUDT_VECTOR_2DI,SetUniform2ip,(JE_GET_CTX,Vector.X,Vector.Y))
		}

		//! Set Cached Setup
		void CShaderVariable_OpenGL::SetCachedSetup(u1 Enable)
		{
			EnableCache = Enable;
		}

		//! Is Cached Setup
		u1 CShaderVariable_OpenGL::IsCachedSetup()
		{
			return EnableCache;
		}

		//! Is Exist In Shader
		u1 CShaderVariable_OpenGL::IsExistInShader()
		{
			for(u8 i=0;i<JE_MAX_SHADER_GLOBAL_TECHNIQUES;i++)
				if(Location[i] != -1)
					return true;
			return false;
		}

		//! Get Locations
		void CShaderVariable_OpenGL::GetLocations()
		{
			if(CurrentWorkMode != OWM_11)
			{
				for(u8 i=0;i<JE_MAX_SHADER_GLOBAL_TECHNIQUES;i++)
				{
					if(!CurrentSP[i])
						continue;

					Location[i] = Extensions->GetUniformLocation(CurrentSP[i],Name.c_str());
					if((Location[i] == -1) && (!CurrentIgnoreLocationErrors))
						JELOG_E(jeStringc("Shader Variable '")+Name+jeStringc("' not found"))
				}
			}
		}

	}
}

#endif
